package Sonnet;

import java.awt.Dimension;

import javafx.application.Application;

import javafx.application.Platform;
import javafx.scene.*;
import javafx.scene.Scene;
import javafx.stage.Stage;

import javafx.fxml.FXMLLoader;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.scene.SnapshotParameters;
import javafx.beans.value.*;
import java.util.concurrent.CountDownLatch.*;
import java.util.concurrent.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import javafx.concurrent.Worker.State;
import javafx.animation.AnimationTimer;
import javafx.concurrent.Task;
import java.util.concurrent.TimeUnit;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.ActionEvent;
import java.awt.Dimension;
import java.lang.Object;
import java.io.InputStream;
import java.io.File;
import javax.imageio.ImageIO;//Keep For .png Image Creation
import java.io.IOException;
import java.nio.ByteBuffer;

import javafx.scene.paint.Color;
import javafx.geometry.Rectangle2D;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.image.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.image.WritableImage.*;
import javafx.scene.input.KeyEvent;
//import javafx.scene.layout.VBox;
import javafx.scene.text.*;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.scene.layout.*;
//import javafx.scene.paint.Color;
//import javafx.scene.shape.*;

//import java.util.Timer;
//import java.util.TimerTask;
import javax.swing.JFileChooser;
//import javafx.stage.DirectoryChooser;
//import javafx.stage.FileChooser;
import javafx.embed.swing.SwingFXUtils;

//import org.junit.After;
//import org.junit.Before;
//import org.junit.Test;
import org.openqa.selenium.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.jcodec.api.awt.SequenceEncoder;//Keep For .mp4 Video Creation

public class Sonnet extends Application
{

	@Override
	public void start(Stage stage) throws IOException
	{
		//Thread thread = new Thread(() ->
		//{
		//	try { Thread.sleep(400); }
		//	catch (InterruptedException exc) { throw new Error("Unexpected interruption", exc); }
		// Update text on FX Application Thread:
		CountDownLatch countDownLatch = new CountDownLatch(1);	
		// This is how you know how much screen you can write to.
		Screen screen = Screen.getPrimary();
		Rectangle2D bounds = screen.getVisualBounds();

		stage.setTitle("Sonnet");
		stage.setWidth(bounds.getWidth());
		stage.setHeight(bounds.getHeight());


		Dimension dimension = new Dimension( (int)bounds.getWidth(), (int)bounds.getHeight() );
		dimension.width = (int)bounds.getWidth();
		dimension.height = (int)bounds.getHeight() + 40;
		Dimension Dim = dimension;

		stage.setFullScreen(true);

		System.out.println(bounds);
		SnapshotParameters sP = new SnapshotParameters();
		sP.setViewport(bounds);

		final WritableImage origionalTextPicture = new WritableImage( Dim.width, Dim.height );

		Platform.runLater(new Runnable()
		{
			@Override
			public void run()
			{
				WebView browser = new WebView();
				WebEngine webEngine = browser.getEngine();
				//webEngine.load("http://www.pixies.zone/PixiesTest.html");

				CountDownLatch countDownLatch = new CountDownLatch(1);
				try { webEngine.getLoadWorker().stateProperty().addListener(
						new ChangeListener<State>() {
							public void changed(ObservableValue ov, State oldState, State newState)
							{
								if (newState == State.SUCCEEDED) {
									final AnimationTimer animationTimer = new AnimationTimer()
									{

										private int pulseCounter;

										@Override
										public void handle(long arg0) {
											pulseCounter++;
											if(pulseCounter > 2) {
												stop();
												browser.snapshot(new SnapshotParameters(), origionalTextPicture);
												//								stage.close();
												countDownLatch.countDown();
											}
										}
									};
									animationTimer.start();
								}
							}
						}
						);
				webEngine.load("http://www.pixies.zone/PixiesTest.html"); }
				catch(ArrayIndexOutOfBoundsException ie2) { ie2.printStackTrace(); }

				try { RainbowSystem(bounds, stage, browser, origionalTextPicture); }
				catch(IOException ie) { ie.printStackTrace(); }
			}
		});

		//countDownLatch.await(10, TimeUnit.SECONDS);
		//return origionalTextPicture;
	};
	//}

	static void RainbowSystem(Rectangle2D bounds, Stage primaryStage, WebView browser, WritableImage origionalTextPicture) throws IOException
	{

		double fRateDivisor = 22;//444 HERE HERE HERE DETERMINES FRAME RATE AND RAINBOW MOVEMENT RATE

		FXMLLoader loader = new FXMLLoader(Sonnet.class.getResource("main.fxml"));


		StackPane stackPane[] = new StackPane[(int)(fRateDivisor + 1)];
		stackPane[0] = new StackPane();
		stackPane[0].setPrefSize(1920, 1080);//stackPane.setPrefSize(1920, 1080);//stackPane.setPrefSize(960, 540);



		//Image image = new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");

		//WebView browser = new WebView();
		//WebEngine webEngine = browser.getEngine();
		//webEngine.load("http://www.pixies.zone/PixiesTest.html");

		//WritableImage origionalTextPicture = new WritableImage( dimension.width, dimension.height );
		//Image origionalBrowserImage = new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");//browser.snapshot(sP, origionalTextPicture);//new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");//
		//Image origionalBrowserImage = new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Test.png");//browser.snapshot(sP, origionalTextPicture);//new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");//
		//Image origionalBrowserImage = browser.snapshot(sP, origionalTextPicture);
		//Image origionalBrowserImage = browser.snapshot(sP, origionalTextPicture);


		JFileChooser jFileChooser = new JFileChooser();
		String path = "C:\\TestVideo.mp4", text = "Sky.Netarianism";//"C:\\TestImage.png"//"C:\\TestVideo.mp4"//"C:\\TEMP_Sonnet.mp4";
		File selectedFile = new File (path);

		jFileChooser.setSelectedFile(selectedFile);
		int returnValue = jFileChooser.showSaveDialog(null);
		if (returnValue == JFileChooser.APPROVE_OPTION)
		{
			selectedFile = jFileChooser.getSelectedFile();
			System.out.println(selectedFile.getName());
			System.out.println(selectedFile.getPath());
		}

		SequenceEncoder enc = new SequenceEncoder(selectedFile);

		Scene scene[] = new Scene[(int)(fRateDivisor + 1)];
		scene[0] = new Scene(stackPane[0]);

		//scene[0].setRoot(browser);

		//Image origionalBrowserImage = scene[0].getRoot().snapshot(sP, origionalTextPicture);


		WebDriver driver = new FirefoxDriver();
		driver.get("http://www.pixies.zone/PixiesTest.html");

		// FirefoxDriver will pass this condition, but just to make sure. For example, you can't take a screenshot with HTMLUnit.
		if (driver instanceof TakesScreenshot) {
		    File tempFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
		    // and now copy the file some place you want it to have
		}
		
		
		BufferedImage bi = SwingFXUtils.fromFXImage(origionalTextPicture, null);
		Image origionalBrowserImage = SwingFXUtils.toFXImage(bi, origionalTextPicture);
		PixelReader origionalpr = origionalBrowserImage.getPixelReader();
		PixelWriter origionalpw = origionalTextPicture.getPixelWriter();
		ImageView origionalImageView = new ImageView();
		origionalImageView.setImage(origionalBrowserImage);

		//scene[0].setRoot(browser);

		stackPane[0].getChildren().add(origionalImageView);
		//stackPane[0].getChildren().add(browser);
		primaryStage.setScene(scene[0]);

		primaryStage.setFullScreen(true);
		primaryStage.show();




		//final WebView browser = new WebView();
		//Scene scene = new Scene(browser);
		// final Stage stage = new Stage();
		// stage.show();
		// stage.setScene(scene);
		//browser.setPrefSize(Dim.width, Dim.height);
		//WebEngine webEngine = browser.getEngine();

		//public void changed(ObservableValue ov, State oldState, State newState)
		//{
		/*	if (newState == State.SUCCEEDED)
			{
				final AnimationTimer animationTimer = new AnimationTimer()
				{

					private int pulseCounter;

					@Override
					public void handle(long arg0)
					{
						pulseCounter++;
						if(pulseCounter > 2)
						{
							stop();
							browser.snapshot(new SnapshotParameters(), origionalTextPicture);
							//					stage.close();
							countDownLatch.countDown();
						}
					}
				};
				animationTimer.start();
			}
		//}
		//webEngine.loadContent(html);

		//countDownLatch.await(10, TimeUnit.SECONDS);//*/







		browser.getEngine().getLoadWorker();
		scene[0].snapshot(origionalTextPicture);

		WritableImage writableImage = origionalTextPicture;
		PixelReader pr = writableImage.getPixelReader();
		PixelWriter pw = writableImage.getPixelWriter();
		ImageView imageView = new ImageView();
		imageView.setImage(writableImage);















		//*/
		double xSlider = 0;
		//try {// new Timer().schedule(new TimerTask() { public void run() {
		ColorSlider black = new ColorSlider( new double[]{0,0,0} );

		ColorSlider color = black;

		ColorSlider white = new ColorSlider( new double[]{255,255,255} );
		ColorSlider skyblue = new ColorSlider( new double[]{134,206,249} );
		ColorSlider pink = new ColorSlider( new double[]{255,64,143} );
		ColorSlider blue = new ColorSlider( new double[]{0,0,192} );
		ColorSlider red = new ColorSlider( new double[]{193,0,0} );
		ColorSlider purple = new ColorSlider( new double[]{122,0,178} );
		ColorSlider orange = new ColorSlider( new double[]{255,134,0} );
		ColorSlider green = new ColorSlider( new double[]{0,133,50} );
		ColorSlider yellow = new ColorSlider( new double[]{246,252,15} );
		ColorSlider teal = new ColorSlider( new double[]{3,224,149} );
		ColorSlider bronze = new ColorSlider( new double[]{152,123,46} );
		ColorSlider silver = new ColorSlider( new double[]{193,192,192} );
		ColorSlider gold = new ColorSlider( new double[]{205,191,44} );
		ColorSlider lime = new ColorSlider( new double[]{126,193,33} );
		ColorSlider brown = new ColorSlider( new double[]{131,103,71} );

		int pixiePink = (int)(255 * 16777216) + (int)(255 * 65536) + (int)(0 * 256) + 255;

		int numberOfColorsInTheRainbow = 7;//asdf Always Worry About This!

		//int [] background = {255, 255, 255};
		//background = new int[Dim.width * Dim.height * 3];
		Dimension HalfedDim = new Dimension(Dim.width / 2, Dim.height / 2);
		//HalfedDim.width = Math.round( Dim.width / 2 );
		//HalfedDim.height = Math.round( Dim.height / 2 );
		double radialDistance = Math.pow(Math.pow(HalfedDim.width + 1, 2) + Math.pow(HalfedDim.height + 1, 2), 0.5);//For Clock And Rainbow Spiral No Whole Thing / 12 That Is For Fractal.
		//double radialVariableDistance = radialDistance;
		double fullAngle = Math.PI * radialDistance;
		double angle = 0;
		double a = 0;
		double radius = 0;
		double colorDisplacement = 0;

		double interval = fullAngle / 2 / fRateDivisor;//Last Division Determines The Frame Rate

		double colorInterval = fullAngle / ( numberOfColorsInTheRainbow - 1 );//For Rainbow Clock And Spiral
		//System.out.println(prWaterMark.getArgb(0, 0));
		while (xSlider < fullAngle / 10 )
		{
			for (int x = 0; x < Dim.width; x++)//x < HalfedDim.width * 2//For old Background Raster ( HalfedDim.width * 2 )// For Fractals And Rainbow Sliders And ??? ///To Test Moving Rainbow From Left To Right
			{
				for (int y = 0; y < Dim.height; y++)//y < HalfedDim.height * 2//For old Background Raster ( HalfedDim.height * 2 )//Comment Out Here For NonFractals
				{
					//radialVariableDistance = Math.pow(Math.pow(x - HalfedDim.width + 1, 2) + Math.pow( y - HalfedDim.height + 1, 2), 0.5);//Comment Out For  Ghetto Many Rainbow Clocks 

					/*if ( y - HalfedDim.height + 1 != 0) //y - HalfedDim.height + 1 < -tolerance || y - HalfedDim.height + 1 > tolerance )// OLD: ( y - HalfedDim.height + 1) != 0 && y != 0 //Use For Old Infinite Number Theorem Down
								angle = Math.atan2( (x - HalfedDim.width + 1) , (y - HalfedDim.height + 1 ) );
							else
								angle = Math.atan2( (x - HalfedDim.width + 1), 0.0002 );// Uncomment For NonRising Sun*/
					angle = Math.atan2( (x - HalfedDim.width + 1) , (y - Dim.height * 4 / 3 + 1 ) );// For Rising Sun + 1
					//	angle = Math.atan( x - HalfedDim.width + 1 / ( y - HalfedDim.height + 1 ) );//WON'T WORK FOR NO GOOD REASON
					//else//WON'T WORK FOR NO GOOD REASON
					//	angle = Math.atan( x - HalfedDim.width + 1 / .0002 );//WON'T WORK FOR NO GOOD REASONRenderedImage
					//	angle = Math.atan2( x - HalfedDim.width + 1, tolerance ); //Adjust The Small Number For Mostly Black Screen

					a = ( angle * /*Math.cos( angle ) * Math.sin( angle ) * Math.pow(radialVariableDistance, 1)*/ Math.tan( angle ) /*+ Math.PI*/ )  * radialDistance / 4.46783504 + xSlider;// + Or - xSlider Determines Which Direction It Moves //For Infinite Number Theorem Good a = ( angle * /*Math.cos( angle ) * Math.sin( angle ) * Math.pow(radialVariableDistance, 1)*/ Math.tan( angle ) + Math.PI )  * radialDistance / 4.46783504 - xSlider;  //a = ( angle /*Math.cos( angle ) * Math.sin( angle ) * Math.pow(radialVariableDistance, 1)*/ Math.tan( angle ) + Math.PI )  * radialDistance / 4.46783504 - xSlider;//Use For Rainbow Spiral Multiply radialDistance By 10 To Increase The Number Of Spirals //Use This: "a = ( - angle + Math.PI ) * radialDistance;" Line For Rainbow Clock //a = ( - angle * Math.tan( angle ) + Math.PI ) * radialDistance - xSlider; For Infinite Number Theorem //a = ( - angle * Math.cos( angle ) * Math.sin( angle ) * Math.pow(radialVariableDistance, 2) + Math.PI ) * radialDistance - xSlider; */
					radius = a * 10;
					// *///For Old Infinite Number Theorem Use Above
					//radius = a * 10;//For Rainbow Spiral And Clock
					//}
					while ( radius < colorDisplacement)//For Rainbow Ring
						radius = radius + fullAngle - colorDisplacement;//BAD COMMENT: For Rainbow Ring Take Out - 38
					while ( radius > fullAngle )//radius > fullAngle For Non Infinite Number Theorem
						radius = radius - fullAngle - colorDisplacement; //radius = radius - fullAngle; For Non Infinite Number Theorem //For Rainbow Clock Comment Out To Here. Here -- */
					if ( radius >= colorDisplacement && radius < (numberOfColorsInTheRainbow - 1) * colorInterval  + colorDisplacement)//For Purple Outside Ring
					{
						if ( origionalpr.getArgb(x, y) == pixiePink )
							if ( radius < colorInterval  + colorDisplacement)//For Normal Rainbow Use This Instead Of The Others
								color.ColorSliderFunction(colorInterval, radius - colorDisplacement, purple, blue);
							else if ( radius < ( 2 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, radius - colorInterval - colorDisplacement, blue, teal);
							else if ( radius < ( 3 * colorInterval  + colorDisplacement) )		
								color.ColorSliderFunction(colorInterval, radius - 2 * colorInterval - colorDisplacement, teal, pink);
							else if ( radius < ( 4 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, radius - 3 * colorInterval - colorDisplacement, pink, yellow);
							else if ( radius < ( 5 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, radius - 4 * colorInterval - colorDisplacement, yellow, orange);
							else
								color.ColorSliderFunction(colorInterval, radius - 5 * colorInterval - colorDisplacement, orange, red);
						else
							if ( radius < colorInterval  + colorDisplacement)//For Normal Rainbow Use This Instead Of The Others
								color.ColorSliderFunction(colorInterval, radius - colorDisplacement, red, orange);
							else if ( radius < ( 2 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, radius - colorInterval - colorDisplacement, orange, yellow);
							else if ( radius < ( 3 * colorInterval  + colorDisplacement) )		
								color.ColorSliderFunction(colorInterval, radius - 2 * colorInterval - colorDisplacement, yellow, green);
							else if ( radius < ( 4 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, radius - 3 * colorInterval - colorDisplacement, green, teal);
							else if ( radius < ( 5 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, radius - 4 * colorInterval - colorDisplacement, teal, blue);
							else
								color.ColorSliderFunction(colorInterval, radius - 5 * colorInterval - colorDisplacement, blue, purple);

						/*if ( radius < colorInterval  + colorDisplacement)//For Normal Rainbow Use This Instead Of The Others
									color.ColorSliderFunction(colorInterval, radius - colorDisplacement, white, skyblue);
								else if ( radius < ( 2 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - colorInterval - colorDisplacement, skyblue, pink);
								else if ( radius < ( 3 * colorInterval  + colorDisplacement) )		
									color.ColorSliderFunction(colorInterval, radius - 2 * colorInterval - colorDisplacement, pink, blue);
								else if ( radius < ( 4 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 3 * colorInterval - colorDisplacement, blue, red);
								else if ( radius < ( 5 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 4 * colorInterval - colorDisplacement, red, purple);
								else if ( radius < ( 6 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 5 * colorInterval - colorDisplacement, purple, orange);
								else if ( radius < ( 7 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 6 * colorInterval - colorDisplacement, orange, green);
								else if ( radius < ( 8 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 7 * colorInterval - colorDisplacement, green, yellow);
								else if ( radius < ( 9 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 8 * colorInterval - colorDisplacement, yellow, teal);
								else if ( radius < ( 10 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 9 * colorInterval - colorDisplacement, teal, bronze);
								else if ( radius < ( 11 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 10 * colorInterval - colorDisplacement, bronze, silver);
								else if ( radius < ( 12 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 11 * colorInterval - colorDisplacement, silver, gold);
								else if ( radius < ( 13 * colorInterval  + colorDisplacement) )
									color.ColorSliderFunction(colorInterval, radius - 12 * colorInterval - colorDisplacement, gold, lime);
								else
									color.ColorSliderFunction(colorInterval, radius - 13 * colorInterval - colorDisplacement, lime, brown);//*/

					}
					//background[3 * x + Dim.width * 3 * y + 0] = (int)color.RGB[0];//background[3 * x + (HalfedDim.width * 6 + 3) * y + 0]
					//background[3 * x + Dim.width * 3 * y + 1] = (int)color.RGB[1];//background[3 * x + (HalfedDim.width * 6 + 3) * y + 1]
					//background[3 * x + Dim.width * 3 * y + 2] = (int)color.RGB[2];//background[3 * x + (HalfedDim.width * 6 + 3) * y + 2]//*/
					pw.setArgb( x, y, (int)(255 * 16777216 + (int)color.RGB[0] * 65536 + (int)color.RGB[1] * 256 + (int)color.RGB[2]));//setArgb
				}
			}

			imageView.setImage(writableImage);

			stackPane[(int)(fRateDivisor * xSlider/fullAngle) + 1] = new StackPane();
			stackPane[(int)(fRateDivisor * xSlider/fullAngle) + 1].getChildren().add(imageView);
			scene[(int)(fRateDivisor * xSlider/fullAngle) + 1] = new Scene(stackPane[(int)(fRateDivisor * xSlider/fullAngle) + 1]);
			primaryStage.setScene(scene[(int)(fRateDivisor * xSlider/fullAngle) + 1]);

			System.out.println(Dim);

			primaryStage.setFullScreen(true);

			primaryStage.show();

			if (xSlider == 0)
			{
				File outputFile = new File("C:/TestImage.png");
				BufferedImage bImage = SwingFXUtils.fromFXImage(scene[(int)(fRateDivisor * xSlider/fullAngle) + 1].snapshot(null), null);
				try {
					ImageIO.write(bImage, "png", outputFile);
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			enc.encodeImage( SwingFXUtils.fromFXImage(scene[(int)(fRateDivisor * xSlider/fullAngle) + 1].snapshot(null), null ) );
			//enc.encodeImage( SwingFXUtils.fromFXImage( imageView.getImage(), null ) );


			xSlider = xSlider + interval;
			System.out.println(Dim);
			//}
			//Thread.sleep(2000);}
			//catch (Exception ex) {ex.printStackTrace();}

		}
		enc.finish();













		System.out.println("Done Making Video");

		/*scene.setOnKeyReleased(new EventHandler<KeyEvent>()
		{
			@Override
			public void handle(KeyEvent event)
			{
				if ( event.isAltDown() == true)
				{
					switch (event.getCode())
					{
						case ENTER: primaryStage.setFullScreen(!primaryStage.isFullScreen()); break;
					}
				}
			}
		});//*/
	}

	public static void main(String[] args) throws IOException
	{
		launch(args);
	}
}